package com.ifonly.activerecord.sql;

import com.ifonly.activerecord.Model;
import com.ifonly.utils.BeanUtils;
import com.ifonly.utils.ReflectUtils;

import java.beans.PropertyDescriptor;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @author <a href="mailto:ifonlymaster@163.com">ifonly</a>
 * @version 1.0 2015-12-21 17:10
 * @since JDK 1.6
 */
public abstract class ResultSetHandler<T> {

    public abstract T handle(ResultSet rs) throws SQLException;


    /**
     * a row ResultSet to a array
     *
     * @param rs row ResultSet
     * @return array
     * @throws SQLException
     */
    public Object[] rowResultSetToArray(ResultSet rs) throws SQLException {
        ResultSetMetaData meta = rs.getMetaData();
        int cols = meta.getColumnCount();
        Object[] result = new Object[cols];

        for (int i = 0; i < cols; i++) {
            result[i] = rs.getObject(i + 1);
        }

        return result;
    }

    /**
     * a row ResultSet to a map
     *
     * @param rs row ResultSet
     * @return Map
     */
    protected Map<String, Object> rowResultSetToMap(ResultSet rs) throws SQLException {
        Map<String, Object> result = new HashMap<String, Object>();
        ResultSetMetaData rsmd = rs.getMetaData();
        int cols = rsmd.getColumnCount();

        for (int i = 1; i <= cols; i++) {
            String columnName = rsmd.getColumnLabel(i);
            if (null == columnName || 0 == columnName.length()) {
                columnName = rsmd.getColumnName(i);
            }
            result.put(columnName, rs.getObject(i));
        }

        return result;
    }

    /**
     * a row of ResultSet to Model
     *
     * @param rs row ResultSet
     * @param clazz Model class
     * @return Model
     */
    protected Model rowResultSetToModel(ResultSet rs, Class<Model> clazz) throws SQLException {
        Model model = ReflectUtils.newInstance(clazz);
        Map<String, Object> propertyAndValues = new HashMap<String, Object>();
        Set<String> columns = model.getColumnNames();
        for (String column : columns) {
            Object value = rs.getObject(column);
            if (value != null) {
                propertyAndValues.put(column, value);
            }
        }
        model.setAll(propertyAndValues);
        return model;
    }

    /**
     * a row of ResultSet to JavaBean
     *
     * @param rs row ResultSet
     * @param clazz JavaBean class
     * @param <T> type of JavaBean
     * @return JavaBean
     * @throws SQLException
     */
    protected <T> T rowResultSetToBean(ResultSet rs, Class<T> clazz) throws SQLException {
        PropertyDescriptor[] props = BeanUtils.propertyDescriptors(clazz);

        ResultSetMetaData rsmd = rs.getMetaData();
        int[] columnToProperty = mapColumnsToProperties(rsmd, props);

        T bean = ReflectUtils.newInstance(clazz);

        for (int i = 1; i < columnToProperty.length; i++) {

            if (columnToProperty[i] == -1) {
                continue;
            }

            PropertyDescriptor prop = props[columnToProperty[i]];
            Object value = rs.getObject(i);

            BeanUtils.setProperty(bean, prop, value);
        }

        return bean;
    }

    protected int[] mapColumnsToProperties(ResultSetMetaData rsmd,
                                           PropertyDescriptor[] props) throws SQLException {

        int cols = rsmd.getColumnCount();
        int[] columnToProperty = new int[cols + 1];
        Arrays.fill(columnToProperty, -1);
        for (int col = 1; col <= cols; col++) {

            String propertyName = getPropertyName(rsmd, col);

            for (int i = 0; i < props.length; i++) {
                if (propertyName.equalsIgnoreCase(props[i].getName())) {
                    columnToProperty[col] = i;
                    break;
                }
            }
        }

        return columnToProperty;
    }

    protected String getPropertyName(ResultSetMetaData rsmd, int columnIndex) throws SQLException {
        String propertyName = rsmd.getColumnLabel(columnIndex);
        if (null == propertyName || 0 == propertyName.length()) {
            propertyName = rsmd.getColumnName(columnIndex);
        }
        return propertyName;
    }

}
